<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN" dir="ltr">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>Grails 插件 - justjavac(风之谷) - ITeye技术网站</title>
    <meta name="description" content="   插件    Grails提供了许多扩展点来满足你的扩展，包括从命令行接口到运行时配置引擎。以下章节详细说明了该如何着手来做这些扩展。    12.1 创建和安装插件  创建插件  创建一个Grails插件，只需要运行如下命令即可：         grails create-plugin [PLUGIN NAME]    根据你输入的名字将产生一插件工程。比如你输入 grails create ..." />
    <meta name="keywords" content="Grails, Groovy, Hibernate, Spring, quartz Grails 插件" />
    <link rel="shortcut icon" href="/images/favicon.ico" type="image/x-icon" />
    <link rel="search" type="application/opensearchdescription+xml" href="/open_search.xml" title="ITeye" />
    <link href="/rss" rel="alternate" title="justjavac(风之谷)" type="application/rss+xml" />
    <link href="http://www.iteye.com/stylesheets/blog.css?1329444322" media="screen" rel="stylesheet" type="text/css" />
<link href="http://www.iteye.com/stylesheets/themes/blog/black.css?1326191326" media="screen" rel="stylesheet" type="text/css" />
    <script src="http://www.iteye.com/javascripts/application.js?1326790556" type="text/javascript"></script>
    <script type="text/javascript">

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-535605-1']);
  _gaq.push(['_setDomainName', 'iteye.com']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();

</script>


      <link href="http://www.iteye.com/javascripts/syntaxhighlighter/SyntaxHighlighter.css?1324994303" media="screen" rel="stylesheet" type="text/css" />
  <script src="http://www.iteye.com/javascripts/syntaxhighlighter/shCoreCommon.js?1325907333" type="text/javascript"></script>
<script src="http://www.iteye.com/javascripts/hotkey.js?1324994303" type="text/javascript"></script>
  <script src="http://www.iteye.com/javascripts/code_favorites.js?1325907333" type="text/javascript"></script>
<script src="http://www.iteye.com/javascripts/weiboshare.js?1324994303" type="text/javascript"></script>
  </head>
  <body>
    <div id="header">
      <div id="blog_site_nav">
  <a href="http://www.iteye.com/" class="homepage">首页</a>
  <a href="http://www.iteye.com/news">资讯</a>
  <a href="http://www.iteye.com/magazines">精华</a>
  <a href="http://www.iteye.com/forums">论坛</a>
  <a href="http://www.iteye.com/ask">问答</a>
  <a href="http://www.iteye.com/blogs">博客</a>
  <a href="http://www.iteye.com/blogs/subjects">专栏</a>
  <a href="http://www.iteye.com/groups">群组</a>
  <a href="#" onclick="return false;" id="msna"><u>更多</u> <small>▼</small></a>
  <div class="quick_menu" style="display:none;">
    <a href="http://www.iteye.com/job">招聘</a>
    <a href="http://www.iteye.com/search">搜索</a>
  </div>
</div>
      <div id="user_nav">
      <a href="/login" class="welcome" title="登录">您还未登录 !</a>
    <a href="/login">登录</a>
    <a href="/signup" class="nobg">注册</a>
  </div>

    </div>

    <div id="page">
      <div id="branding" class="clearfix">
        <div id="blog_name">
          <h1><a href="/">justjavac(风之谷)</a></h1>
        </div>
        <div id='fd'></div>
        <div id="blog_navbar">
          <ul>
            <li class='blog_navbar_for'><a href="http://justjavac.iteye.com"><strong>博客</strong></a></li>
            <li ><a href="/weibo">微博</a></li>
            <li ><a href="/album">相册</a></li>
            <li ><a href="/link">收藏</a></li>
            <li ><a href="/blog/guest_book">留言</a></li>
            <li ><a href="/blog/profile">关于我</a></li>
          </ul>
    
          <div class="search">
            <form action="/blog/search" method="get">
              <input class="search_text" id="query" name="query" style="margin-left: 10px;width: 110px;" type="text" />
              <input class="submit_search" type="submit" value="" />
            </form>
          </div> 
          <div id="fd"></div>         
        </div>
      </div>
      
      <div id="content" class="clearfix">
        <div id="main">
          



          


<div class="blog_main">
  <div class="blog_title">
    <h3>
      <a href="/blog/705556">Grails 插件</a>
      <em class="actions">      </em>
    </h3>
    <ul class='blog_categories'><strong>博客分类：</strong> <li><a href="/category/29623">Groovy&amp;Grails</a></li> </ul>
        <div class='news_tag'><a href="http://www.iteye.com/blogs/tag/Grails">Grails</a><a href="http://www.iteye.com/blogs/tag/Groovy">Groovy</a><a href="http://www.iteye.com/blogs/tag/Hibernate">Hibernate</a><a href="http://www.iteye.com/blogs/tag/Spring">Spring</a><a href="http://www.iteye.com/blogs/tag/quartz">quartz</a></div>
  </div>

  <div id="blog_content" class="blog_content">
    <p> </p>
<h1><a name="12.%20Plug-ins">插件</a></h1>
<p>
Grails提供了许多扩展点来满足你的扩展，包括从命令行接口到运行时配置引擎。以下章节详细说明了该如何着手来做这些扩展。
</p>
<h2><a name="12.1%20Creating%20and%20Installing%20Plug-ins">12.1 创建和安装插件</a></h2>
<h4>创建插件</h4>
<p class="paragraph">创建一个Grails插件，只需要运行如下命令即可：
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>grails create-plugin [PLUGIN NAME]</pre>
</div>
<p class="paragraph">根据你输入的名字将产生一插件工程。比如你输入 <code>grails create-plugin 
example</code>. 系统将创建一个名为 <code>example</code>的插件工程. 
</p>
<p class="paragraph">除了插件的根目录有一个所谓的“插件描述”的Groovy文件外，其他的跟一般的Grails工程结构完全一样.
</p>
<p class="paragraph">将插件作为一个常规的Grails工程是有好处的，比如你可以马上用以下命令来测试你的插件:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>grails run-app</pre>
</div>
<p class="paragraph"> </p>
<blockquote class="note">由于你创建插件默认是没有 <a class="guide">URL 
映射</a>的,因此控制器并不会马上有效.如果你的插件需要控制器，那要创建 
<code>grails-app/conf/MyUrlMappings.groovy</code> 文件,并且在起始位置增加缺省的映射 
<code>"/$controller/$action?/$id?"()</code>. </blockquote>
<p class="paragraph">插件描述文件本身需要符合以 <code>GrailsPlugin</code> 
结尾的惯例并且将位于插件工程的根目录中。比如:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>class ExampleGrailsPlugin {
   def version = 0.1<p class="paragraph">   …
}</p></pre>
</div>
<p class="paragraph">所有插件的根目录下边都必须有此类并且还要有效，此类中定义了插件的版本和其他各式各样的可选的插件扩展点的钩子（hooks）--即插件预留的可以扩展的接口.
</p>
<p class="paragraph">通过以下特殊的属性，你还可以提供插件的一些额外的信息: 
</p>
<ul class="star">
<li>
<code>title</code> - 用一句话来简单描述你的插件 
</li>
<li>
<code>author</code> - 插件的作者 
</li>
<li>
<code>authorEmail</code> - 插件作者的电子邮箱 
</li>
<li>
<code>description</code> - 插件的完整特性描述 
</li>
<li>
<code>documentation</code> - 插件文档的URL </li>
</ul>
<p class="paragraph">以 <a href="http://grails.org/Quartz+plugin:" target="blank">Quartz Grails plugin</a>为例：
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>class QuartzGrailsPlugin {
    def version = <span class="java-quote">"0.1"</span>
    def author = <span class="java-quote">"Sergey Nebolsin"</span>
    def authorEmail = <span class="java-quote">"nebolsin@gmail.com"</span>
    def title = <span class="java-quote">"This plugin adds Quartz job scheduling features to Grails application."</span>
    def description = '''
Quartz plugin allows your Grails application to schedule jobs to be
executed using a specified interval or cron expression. The underlying
system uses the Quartz Enterprise Job Scheduler configured via Spring,
but is made simpler by the coding by convention paradigm.
'''
    def documentation = <span class="java-quote">"http://grails.org/Quartz+plugin"</span><p class="paragraph">   …
}</p></pre>
</div>
<p class="paragraph"> </p>
<h4>插件的安装和发布</h4>
<p class="paragraph">要发布插件，你需要一个命令行窗口，并且进入到插件的根目录，输入:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>grails <span class="java-keyword">package</span>-plugin</pre>
</div>
<p class="paragraph">这将创建一个 <code>grails-</code> +插件名称+版本的zip文件. 
以先前的example插件为例，这个文件名是 <code>grails-example-0.1.zip</code>. 
<code>package-plugin</code> 命令还将生成 <code>plugin.xml</code> 
f在此文件中包含机器可读的插件信息，比如插件的名称、版本、作者等等。
</p>
<p class="paragraph">产生了可以发布的插件文件以后（zip文件），进入到你自己的Grails工程的根目录，输入:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>grails install-plugin /path/to/plugin/grails-example-0.1.zip</pre>
</div>
<p class="paragraph">如果你的插件放在远程的Http服务器上，你也可以这样:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>grails install-plugin http://myserver.com/plugins/grails-example-0.1.zip</pre>
</div>
<p class="paragraph"> </p>
<h4>注意被排除的组件</h4>
<p class="paragraph">尽管 <a class="commandLine">create-plugin</a> 
命令为您创建某些文件，以便插件能做为Grails应用运行，但是当打包插件的时候不是所有的文件都会在含在里面. 以下是通过<a class="commandLine">package-plugin</a>创建时，不包含的文件和目录： 
</p>
<ul class="star">
<li>
<code>grails-app/conf/DataSource.groovy</code> 
</li>
<li>
<code>grails-app/conf/UrlMappings.groovy</code> 
</li>
<li>
<code>grails-app/conf/DataSource.groovy</code> 
</li>
<li>
<code>build.xml</code> 
</li>
<li>Everything within <code>/web-app/WEB-INF</code> </li>
</ul>
<p class="paragraph">如果你希望创建包含 <code>WEB-INF</code> 目录的组建，那么建议你使用 
<code>_Install.groovy</code> 脚本文件 (covered 
later)，这个脚本文件之后会解释；当安装一个插件提供这些组件时，这个脚本文件会被执行。 此外，除了用 
<code>UrlMappings.groovy</code>之外，也允许你使用包括 <code>UrlMappings</code> 
名字来定义不同的名称，例如 <code>FooUrlMappings.groovy</code> 
</p>
<p class="paragraph"> </p>
<p class="paragraph"> </p>
<h2><a name="12.2%20Plugin%20Repositories">12.2 插件仓库</a></h2>
<h4>在Grails插件的存储仓库（Repository）发布插件</h4>
<p class="paragraph">更好的发布插件的方式是将其发布到Grails插件的存储仓库. 这样通过 <a class="commandLine">list-plugins</a> 命令就可以看到你的插件了:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>grails list-plugins</pre>
</div>
<p class="paragraph">此命令将列出Grails插件存储库的所有插件，当然了也可以用 <a class="commandLine">plugin-info</a> 来查看指定插件的信息:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>grails plugin-info [plugin-name]</pre>
</div>
<p class="paragraph">这将输出更多的详细信息，这些信息都是维护在插件描述文件中的。
</p>
<p class="paragraph"> </p>
<blockquote class="note">如果你创建了一个Grails插件，你可以访问 <a href="http://grails.org/Creating+Plugins" target="blank">创建插件</a>，这里详细说明了如何在容器中发布你的插件。 </blockquote>
<p class="paragraph">当你有访问Grails插件仓库的权限时，要发行你的插件，只需要简单执行 <a class="commandLine">release-plugin</a> 即可:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>grails release-plugin</pre>
</div>
<p class="paragraph">这将自动地将改动提交到SVN和创建标签（svn的tagging），并且通过 <a class="commandLine">list-plugins</a> 命令你可以看到这些改动.
</p>
<p class="paragraph"> </p>
<h4>配置附加库</h4>
<p class="paragraph">默认情况下，您使用的 <a class="commandLine">list-plugins</a>, <a class="commandLine">install-plugin</a> and <a class="commandLine">release-plugin</a> 命令都指向 
http://plugins.grails.org。
</p>
<p class="paragraph">然而, 
要配置多个插件仓库，您可以使用<code>grails-app/conf/BuildSettings.groovy</code> 文件:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>grails.plugin.repos.discovery.myRepository=<span class="java-quote">"http://svn.codehaus.org/grails/trunk/grails-test-plugin-repo"</span>
grails.plugin.repos.distribution.myRepository=<span class="java-quote">"https://svn.codehaus.org/grails/trunk/grails-test-plugin-repo"</span></pre>
</div>
<p class="paragraph">Repositories are split into those used for discovery over 
HTTP and those used for distribution, typically over HTTPS. 
如果你想在多个项目中使用相同的设置，你可以把这些配置到 <code>USER_HOME/.grails/settings.groovy</code>。
</p>
<p class="paragraph">一旦使用了 <a class="commandLine">list-plugins</a>, <a class="commandLine">install-plugin</a> and <a class="commandLine">plugin-info</a> 
命令将会自动处理最新配置的插件库。如果你只想把插件库中的插件列表列出来，你可以使用别名：
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>grails list-plugins -repository=myRepository</pre>
</div>
<p class="paragraph">此外，如果你想和配置好的插件包一起发布插件，你可以用 <a class="commandLine">release-plugin</a> 命令：
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>grails release-plugin -repository=myRepository</pre>
</div>
<p class="paragraph"> </p>
<h2><a name="12.3%20Understanding%20a%20Plug-ins%20Structure">12.3 
理解插件的结构</a></h2>
<p>
如前所提到的，一个插件除了包含一个插件描述文件外，几乎就是一个常规的Grails应用。尽管如此，当安装以后，插件的结构还是有些许的差别。比如一个插件目录的结构如下:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>+ grails-app
     + controllers
     + domain
     + taglib
     etc.
 + lib
 + src
     + java
     + groovy
 + web-app
     + js
     + css</pre>
</div>
<p class="paragraph">从本质上讲，当一个插件被安装到Grails工程以后, <code>grails-app</code> 
下边的内容将被拷贝到以 <code>plugins/example-1.0/grails-app</code>(以example为例)目录中. 这些内容 
<strong class="bold">不会</strong> 被拷贝到工程的源文件主目录，即插件永远不会跟工程的主目录树有任何接口上的关系。.
</p>
<p class="paragraph">然而，那些在特定插件目录中 <code>web-app</code> 目录下的静态资源将会被拷贝到主工程的 
<code>plugins</code> 目录下. 比如 <code>web-app/plugins/example-1.0/js</code>. 
</p>
<p class="paragraph">因此，要从正确的地方引用这些静态资源也就成为插件的责任。比如，你要在GSP中引用一个JavaScript文件，你可以这样:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre><span class="xml-tag">&lt;g:createLinkTo dir=<span class="xml-quote">"/plugins/example/js"</span> file=<span class="xml-quote">"mycode.js"</span> /&gt;</span></pre>
</div>
<p class="paragraph">这样做当然可以，但是当你开发插件并且单独运行插件的时候，将产生相对链接（link）的问题.
</p>
<p class="paragraph">为了应对这种变化即不管插件是单独运行还是在Grails应用中运行，特地新增一个特别的 
<code>pluginContextPath</code> 变量，用法如下:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre><span class="xml-tag">&lt;g:createLinkTo dir=<span class="xml-quote">"${pluginContextPath}/js"</span> file=<span class="xml-quote">"mycode.js"</span> /&gt;</span></pre>
</div>
<p class="paragraph">这样在运行期间 <code>pluginContextPath</code> 变量将会等价于/ 或 
<code>/plugins/example</code> 这取决于插件是单独运行还是被安装在Grails应用中
</p>
<p class="paragraph">在lib和 <code>src/java</code> 以及 <code>src/groovy</code> 
下的Java、Groovy代码将被编译到当前工程的 <code>web-app/WEB-INF/classes</code> 
下边，因此在运行时也不会出现类找不到的问题. 
</p>
<h2><a name="12.4%20Providing%20Basic%20Artefacts">12.4 提供基础的工件</a></h2>
<h4>增加新的脚本</h4>
<p class="paragraph">在插件的scripts目录下可以增加新的Gant相关的脚本:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>+ MyPlugin.groovy
   + scripts     &lt;-- additional scripts here
   + grails-app
        + controllers
        + services
        + etc.
    + lib</pre>
</div>
<p class="paragraph"> </p>
<h4>增加新的控制器，标签库或者服务</h4>
<p class="paragraph">在 <code>grails-app</code> 
相关的目录树下，可以增加新的控制器、标签库、服务等，不过要注意：当插件被安装后将从其被安装的地方加载，而不是被拷贝到当前主应用工程的相应目录。. 
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>+ ExamplePlugin.groovy
   + scripts
   + grails-app
        + controllers  &lt;-- additional controllers here
        + services &lt;-- additional services here
        + etc.  &lt;-- additional XXX here
    + lib</pre>
</div>
<p class="paragraph"> </p>
<h4>Providing Views, Templates and View resolution</h4>
<p class="paragraph">提供控制器的插件也会提供默认的视图。通过插件模块化您的应用是个很好的途径。Grails视图处理机制的工作原理是首先查看应用中被安装的视图，如果失败将视图查找插件中的视图。
</p>
<p class="paragraph">比如有一个 <code>AmazonGrailsPlugin</code> 插件提供一个叫 
<code>BookController</code> 的控制器，如果执行了 <code>list</code> 将会首先查找 
<code>grails-app/views/book/list.gsp</code> 这个视图，如果失败，将会在插件里查找相同名称的视图。
</p>
<p class="paragraph">但是，如果视图使用了模板，同时插件也提供了这个视图，那么必须使用以下的语法：
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>&lt;g:render template=<span class="java-quote">"fooTemplate"</span> contextPath=<span class="java-quote">"${pluginContextPath}"</span>/&gt;</pre>
</div>
<p class="paragraph">注意 <code>pluginContextPath</code> 变量做为 
<code>contextPath</code> 属性值的用法。如果没有指定这个属性，Grails将在应用中的模板中查找。
</p>
<p class="paragraph"> </p>
<h4>Excluded Artefacts</h4>
<p class="paragraph">默认的，when packaging a plug-in，当打包一个插件时，Grails 的插件包中将不包含以下文件： 
</p>
<ul class="star">
<li>grails-app/conf/DataSource.groovy 
</li>
<li>grails-app/conf/UrlMappings.groovy 
</li>
<li>Everything under web-app/WEB-INF </li>
</ul>
<p class="paragraph">如果你的插件需要 <code>web-app/WEB-INF</code> 目录下的文件，那么建议你修改插件的 
<code>scripts/_Install.groovy</code> Gant 脚本文件把项目的目标目录安装到插件包中。
</p>
<p class="paragraph">此外, <code>UrlMappings.groovy</code> 
文件默认不会避免命名冲突，你可以使用在默认名字前加增加 <strong class="bold">前缀</strong>。比如叫做 
<code>grails-app/conf/BlogUrlMappings.groovy</code>。 
</p>
<h2><a name="12.5%20Evaluating%20Conventions">12.5 
评估规约</a></h2>
<p>
在得以继续查看基于规约所能提供的运行时配置以前，有必要了解一下怎样来评估插件的这些基本规约。本质上，每一个插件都有一个隐含的 <a class="api">GrailsApplication</a>接口的实例变量：<code>application</code>。
</p>
<p class="paragraph"><code>GrailsApplication</code> 
提供了在工程内评估这些规约的方法并且保存着所有类的相互引用，这些类都实现了 <a class="api">GrailsClass</a> 
接口.
</p>
<p class="paragraph">一个 <code>GrailsClass</code> 
代表着一个物理的Grails资源，比如一个控制器或者一个标签库。如果要获取所有 <code>GrailsClass</code> 实例，你可以这样:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>application.allClasses.each { println it.name }</pre>
</div>
<p class="paragraph">在 <code>GrailsApplication</code> 
实例中有一些特殊的属性可以方便的操作你感兴趣的人工制品（artefact）类型，比如你要获取所有控制器的类，可以如此： 
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>application.controllerClasses.each { println it.name }</pre>
</div>
<p class="paragraph">这些动态方法的规约如下: 
</p>
<ul class="star">
<li>
<code>*Classes</code> - 获取特定人工制品名称的所有类，比如 
<code>application.controllerClasses</code>. 
</li>
<li>
<code>get*Class</code> - 获取特定人工制品的特定类，比如 
<code>application.getControllerClass("ExampleController")</code> 
</li>
<li>
<code>is*Class</code> - 如果给定的类是指定的人工制品类型，那么返回true， 比如 
<code>application.isControllerClass(ExampleController.class)</code> 
</li>
<li>
<code>add*Class</code> - 为给定的人工制品类型新增一个类并且返回新增的 <code>GrailsClass</code> 
实例-比如：<code>application.addControllerClass(ExampleController.class)</code> 
</li>
</ul>
<p class="paragraph"><code>GrailsClass</code> 
接口本身也提供了很多有用的方法以允许你进一步的评估和了解这些规约，他们包括: 
</p>
<ul class="star">
<li>
<code>getPropertyValue</code> - 获取给定属性的初始值 
</li>
<li>
<code>hasProperty</code> - 如果类含有指定的属性，那么返回true 
</li>
<li>
<code>newInstance</code> - 创建一个类的新实例 
</li>
<li>
<code>getName</code> - 如果可以的话，返回应用类的逻辑名称，此名称不含后缀部分 
</li>
<li>
<code>getShortName</code> - 返回类的简称，不包含包前缀 
</li>
<li>
<code>getFullName</code> - 返回应用类的完整名称，包含后缀部分和包的名称 
</li>
<li>
<code>getPropertyName</code> - 将类的名称返回为属性名称 
</li>
<li>
<code>getLogicalPropertyName</code> - 如果可以的话，返回应用类的逻辑属性名称，此名称不包含后缀部分 
</li>
<li>
<code>getNaturalName</code> - 返回属性名称的自然语言的术语（比如将'lastName' 变为 'Last Name'） 
</li>
<li>
<code>getPackageName</code> - 返回包的名称 </li>
</ul>
<p class="paragraph">完整的索引请参考 <a class="api">javadoc 
API</a>.
</p>
<h2><a name="12.6%20Hooking%20into%20Build%20Events">12.6 参与构建事件</a></h2>
<h4>安装后进行配置和参与升级操作</h4>
<p class="paragraph">Grails插件可以在安装完后进行配置并且可以参与应用的升级过程（通过 <a class="commandLine">upgrade</a>命令）,这是由scripts目录下两个特定名称的脚本来完成的： 
- <code>_Install.groovy</code> 和 <code>_Upgrade.groovy</code>. 
</p>
<p class="paragraph"><code>_Install.groovy</code> 是在插件安装完成后被执行的，而 
<code>_Upgrade.groovy</code> 是用户每次通过 <a class="commandLine">upgrade</a> 命令来升级他的应用时被执行的.
</p>
<p class="paragraph">这些是一个普通的 <a class="guide">Gant</a> 
脚本，因此你完全可以使用Gant的强大特性。另外 <code>pluginBasedir</code> 被加入到Gant的标准变量中，其指向安装插件的根目录。
</p>
<p class="paragraph">以下的 <code>_Install.groovy</code> 示例脚本将在 
<code>grails-app</code> 目录下创建一个新的目录，并且安装一个配置模板，如下:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>Ant.mkdir(dir:<span class="java-quote">"${basedir}/grails-app/jobs"</span>)
Ant.copy(file:<span class="java-quote">"${pluginBasedir}/src/samples/SamplePluginConfiguration.groovy"</span>,
         todir:<span class="java-quote">"${basedir}/grails-app/conf"</span>)<p class="paragraph">// To access Grails home you can use following code:
// Ant.property(environment:<span class="java-quote">"env"</span>)
// grailsHome = Ant.antProject.properties.<span class="java-quote">"env.GRAILS_HOME"</span></p></pre>
</div>
<p class="paragraph"> </p>
<h4>脚本事件</h4>
<p class="paragraph">将插件和命令行的脚本事件关联起来还是有可能的，这些事件在执行Grails的任务和插件事件的时候被触发。 
</p>
<p class="paragraph">比如你希望在更新的时候，显示更新状态（如"Tests passed", "Server 
running"），并且创建文件或者人工制品。
</p>
<p class="paragraph">一个插件只能通过 <code>Events.groovy</code> 脚本来监听那些必要的事件。更多详细信息请参考 <a class="guide">Hooking into 
Events</a>. 
</p>
<h2><a name="12.7%20Hooking%20into%20Runtime%20Configuration">12.7 运行时配置中的钩子Hooking into 
Runtime Configuration</a></h2>
<p>
Grails提供了很多的钩子函数来处理系统的不同部分，并且通过惯例的形式来执行运行时配置。
</p>
<p class="paragraph"> </p>
<h4>跟Grails的Spring配置进行交互</h4>
<p class="paragraph">首先你可以使用 <code>doWithSpring</code> 
闭包来跟Grails运行时的配置进行交互，例如下面的代码片段是取自于Grails核心插件 <a class="guide">i18n</a>的一部分:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre><span class="java-keyword">import</span> org.springframework.web.servlet.i18n.CookieLocaleResolver;
<span class="java-keyword">import</span> org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
<span class="java-keyword">import</span> org.springframework.context.support.ReloadableResourceBundleMessageSource;<p class="paragraph">class I18nGrailsPlugin {</p><p class="paragraph">      def version = 0.1</p><p class="paragraph"> def doWithSpring = {
                messageSource(ReloadableResourceBundleMessageSource) {
                        basename = <span class="java-quote">"WEB-INF/grails-app/i18n/messages"</span>
                }
                localeChangeInterceptor(LocaleChangeInterceptor) {
                        paramName = <span class="java-quote">"lang"</span>
                }
                localeResolver(CookieLocaleResolver)
        }
}</p></pre>
</div>
<p class="paragraph">这个插件建立起了Grails <code>messageSource</code> 
bean和一对其他beans以管理Locale解释和更改。它使用 <a class="guide">Spring Bean Builder</a> 语法。
</p>
<p class="paragraph"> </p>
<h4>参与web.xml的生成</h4>
<p class="paragraph">Grails是在加载的时候生成 <code>WEB-INF/web.xml</code> 
文件，因此插件不能直接修改此文件，但他们可以参与此文件的生成。 本质上一个插件可以通过 <code>doWithWebDescriptor</code> 
闭包来完成此功能，此闭包的参数是 <code>web.xml</code> 是作为 <code>XmlSlurper</code> 
<code>GPathResult</code>类型传入的. 
</p>
<p class="paragraph">考虑如下来自 <code>ControllersPlugin</code>的示例:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>def doWithWebDescriptor = { webXml -&gt;
        def mappingElement = webXml.'servlet-mapping'
        mappingElement + {
                'servlet-mapping' {
                        'servlet-name'(<span class="java-quote">"grails"</span>)
                        'url-pattern'(<span class="java-quote">"*.dispatch"</span>)
                }
        }
}</pre>
</div>
<p class="paragraph">此处插件得到最后一个 
<code>&lt;servlet-mapping&gt;</code>元素的引用，并且在其后添加Grails' 
servlet，这得益于XmlSlurper可以通过闭包以编程的方式修改XML的能力。
</p>
<p class="paragraph"> </p>
<h4>在初始化完毕后进行配置</h4>
<p class="paragraph">有时候在Spring的 <a class="api" href="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/context/ApplicationContext.html">ApplicationContext</a> 
被创建以后做一些运行时配置是有意义的，这种情况下，你可以定义 <code>doWithApplicationContext</code> 闭包，如下例：
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>class SimplePlugin {
     def name=<span class="java-quote">"simple"</span>
     def version = 1.1<p class="paragraph">     def doWithApplicationContext = { appCtx -&gt;
          SessionFactory sf = appCtx.getBean(<span class="java-quote">"sessionFactory"</span>)
          // <span class="java-keyword">do</span> something here with session factory
         }
}</p></pre>
</div>
<p class="paragraph"> </p>
<h2><a name="12.8%20Adding%20Dynamic%20Methods%20at%20Runtime">12.8 运行时添加动态方法</a></h2>
<h4>基础知识</h4>
<p class="paragraph">Grails插件允许你在运行时注册Grails管辖类或者其他类的动态方法，但新的方法只能通过 
<code>doWithDynamicMethods</code> 闭包来增加。
</p>
<p class="paragraph">对Grails管辖类来说，比如controllers、tag libraries等等，你可以增加方法，构造函数等，这是通过 
<a href="http://groovy.codehaus.org/ExpandoMetaClass" target="blank">ExpandoMetaClass</a> 机制做到的，比如访问每个控制器的 <a target="blank">MetaClass</a>的代码如下所示： 
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>class ExamplePlugin {
  def doWithDynamicMethods = { applicationContext -&gt;
        application.controllerClasses.each { controllerClass -&gt;
             controllerClass.metaClass.myNewMethod = {-&gt; println <span class="java-quote">"hello world"</span> }
        }
  }
}</pre>
</div>
<p class="paragraph">此处我们通过隐含的application对象来获取所有控制器类的MetaClass实例，并且为每一个控制器增加一个 
<code>myNewMethod</code> 的方法。或者，你已经知道要处理的类的类型了，那你只需要在此类的 <code>metaClass</code> 
属性上增加一个方法即可，代码如下:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>class ExamplePlugin {<p class="paragraph">  def doWithDynamicMethods = { applicationContext -&gt;
      <span class="java-object">String</span>.metaClass.swapCase = {-&gt;
           def sb = <span class="java-keyword">new</span> <span class="java-object">StringBuffer</span>()
           delegate.each {
               sb &lt;&lt; (<span class="java-object">Character</span>.isUpperCase(it as <span class="java-object">char</span>) ?
                      <span class="java-object">Character</span>.toLowerCase(it as <span class="java-object">char</span>) :
                      <span class="java-object">Character</span>.toUpperCase(it as <span class="java-object">char</span>))
           }
           sb.toString()
      }</p><p class="paragraph">      assert <span class="java-quote">"UpAndDown"</span> == <span class="java-quote">"uPaNDdOWN"</span>.swapCase()
  }
}</p></pre>
</div>
<p class="paragraph">此例中，我们直接在 <code>java.lang.String</code> 的 
<code>metaClass</code> 上增加一个新的 <code>swapCase</code> 方法. 
</p>
<p class="paragraph"> </p>
<h4>跟ApplicationContext交互</h4>
<p class="paragraph"><code>doWithDynamicMethods</code> 闭包的参数是Spring的 
<code>ApplicationContext</code> 
实例，这点非常有用，因为这允许你和该应用上下文实例中的对象进行交互。比如你打算实现一个跟Hibernate交互的方法，那你可以联合着 
<code>HibernateTemplate</code>来使用<code>SessionFactory</code> 例，代码如下:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre><span class="java-keyword">import</span> org.springframework.orm.hibernate3.HibernateTemplate<p class="paragraph">class ExampleHibernatePlugin {</p><p class="paragraph">   def doWithDynamicMethods = { applicationContext -&gt;</p><p class="paragraph">       application.domainClasses.each { domainClass -&gt;</p><p class="paragraph">           domainClass.metaClass.<span class="java-keyword">static</span>.load = { <span class="java-object">Long</span> id-&gt;
                def sf = applicationContext.sessionFactory
                def template = <span class="java-keyword">new</span> HibernateTemplate(sf)
                                template.load(delegate, id)
           }
       }
   }
}</p></pre>
</div>
<p class="paragraph">另外因为Spring容器具有自动装配和依赖注入的能力，你可以在运行时实现更强大的动态构造器，此构造器使用applicationContext来装配你的对象及其依赖：
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>class MyConstructorPlugin {<p class="paragraph">    def doWithDynamicMethods = { applicationContext -&gt;
         application.domainClasses.each { domainClass -&gt;
              domainClass.metaClass.constructor = {-&gt;
                  <span class="java-keyword">return</span> applicationContext.getBean(domainClass.name)
              }
         }</p><p class="paragraph">    }
}</p></pre>
</div>
<p class="paragraph">这里我们实际做的是通过查找Spring的原型beans（prototyped beans）来替代缺省的构造器。
</p>
<p class="paragraph"> </p>
<h2><a name="12.9%20Participating%20in%20Auto%20Reload%20Events">12.9 参与自动重载</a></h2>
<h4>监控资源的改变</h4>
<p class="paragraph">通常来讲，当资源发生改变的时候，监控并且重新加载这些变化是非常有意义的。这也是Grails为什么要在运行时实现复杂的应用程序重新加载。查看如下Grails的 
<code>ServicesPlugin</code>的一段简单的代码片段: 
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>class ServicesGrailsPlugin {
    …
    def watchedResources = <span class="java-quote">"file:./grails-app/services/*Service.groovy"</span><p class="paragraph">    …
  def onChange = { event -&gt;
    <span class="java-keyword">if</span>(event.source) {
      def serviceClass = application.addServiceClass(event.source)
      def serviceName = <span class="java-quote">"${serviceClass.propertyName}"</span>
      def beans = beans {
           <span class="java-quote">"$serviceName"</span>(serviceClass.getClazz()) { bean -&gt;
               bean.autowire =  <span class="java-keyword">true</span>
      }
            }
      <span class="java-keyword">if</span>(event.ctx) {
        event.ctx.registerBeanDefinition(serviceName,
                                       beans.getBeanDefinition(serviceName))
      }
    }
  }
}</p></pre>
</div>
<p class="paragraph">首先定义了 <code>watchedResources</code> 
集合，此集合可能是String或者String的List，包含着要监控的资源的引用或者模式。如果要监控的资源是Groovy文件，那当它被改变的时候，此文件将会自动被重新加载，而且被传给 
<code>onChange</code> 闭包的参数 <code>event</code> . 
</p>
<p class="paragraph"><code>event</code> 对象定义了一些有益的属性: 
</p>
<ul class="star">
<li>
<code>event.source</code> - The source of the event which is either the 
reloaded class or a Spring Resource 
</li>
<li>
<code>event.ctx</code> - The Spring <code>ApplicationContext</code> instance 
</li>
<li>
<code>event.plugin</code> - The plugin object that manages the resource 
(Usually this) 
</li>
<li>
<code>event.application</code> - The <code>GrailsApplication</code> instance 
</li>
</ul>
<p class="paragraph">通过这些对象，你可以评估这些惯例,而且基于这些惯例你可以将这些变化适当的应用到 
<code>ApplicationContext</code> 中, 
在上述的"Services"示例中，当一个service类变化时，一个新的service类被重新注册到 
<code>ApplicationContext</code> 中.
</p>
<p class="paragraph"> </p>
<h4>影响其他插件</h4>
<p class="paragraph">当一个插件变化时，插件不但要有相应地反应，而且有时还会“影响”另外的插件。 
</p>
<p class="paragraph">以Services 和 Controllers插件为例. 
当一个service被重新加载的时候，除非你也重新加载controllers，否则你将加载过的service自动装配到旧的controller类的时候，将会发生问题。.
</p>
<p class="paragraph">为了避免这种情况发生，你可以指定将要受到“影响”的另外一个插件，这意味着当一个插件监测到改变的时候，它将先重新加载自身，然后重新加载它所影响到的所有插件。看 
<code>ServicesGrailsPlugin</code>的代码片段: 
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>def influences = ['controllers']</pre>
</div>
<p class="paragraph"> </p>
<h4>观察其他插件</h4>
<p class="paragraph">如果你想观察一个特殊的插件的变化但又不需要监视插件的资源，那你可以使用"observe"属性:
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>def observe = [<span class="java-quote">"hibernate"</span>]</pre>
</div>
<p class="paragraph">在此示例中，当一个Hibernate的领域类变化的时候，你将收到从hibernate插件传递过来的事件。你也可以使用一个通配符查看所有加载的插件：
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>def observe = [<span class="java-quote">"*"</span>]</pre>
</div>
<p class="paragraph">Logging plugin不仅如此，当应用运行时它都能添加 <code>log</code> 属性到 <em class="italic">任何</em> 插件库。
</p>
<h2><a name="12.10%20Understanding%20Plug-in%20Load%20Order">12.10 理解插件加载的顺序</a></h2>
<h4>Controlling Plug-in Dependencies</h4>
<p class="paragraph">插件经常依赖于其他已经存在的插件，并且也能调整这种依赖. 为了做到这点，一个插件可以定义两个属性，首先是 
<code>dependsOn</code>.让我们看看Grails Hibernate插件的代码片段：
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>class HibernateGrailsPlugin {
        def version = 1.0
        def dependsOn = [dataSource:1.0,
                         domainClass:1.0,
                         i18n:1.0,
                         core: 1.0]<p class="paragraph">}</p></pre>
</div>
<p class="paragraph">如上述示例所演示的，Hibernate插件依赖于4个插件： <code>dataSource</code> , 
<code>domainClass</code>, <code>i18n</code> 和 <code>core</code>. 
</p>
<p class="paragraph">根本上讲，这些被依赖的插件将先被加载，接着才是Hibernate插件，如果这些被依赖的插件没有加载，那么Hibernate也不会加载。 
</p>
<p class="paragraph"><code>dependsOn</code>属性也支持一个小型的表达语言指定版本范围。以下是一些简单的语法例子：
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>def dependsOn = [foo:<span class="java-quote">"* &gt; 1.0"</span>]
def dependsOn = [foo:<span class="java-quote">"1.0 &gt; 1.1"</span>]
def dependsOn = [foo:<span class="java-quote">"1.0 &gt; *"</span>]</pre>
</div>
<p class="paragraph">当使用*通配符的时候，它表示"任何"版本。 The expression syntax also excludes any 
suffixes such as -BETA, -ALPHA etc. so for example the expression "1.0 &gt; 1.1" 
would match any of the following versions: 
</p>
<ul class="star">
<li>1.1 
</li>
<li>1.0 
</li>
<li>1.0.1 
</li>
<li>1.0.3-SNAPSHOT 
</li>
<li>1.1-BETA2 </li>
</ul>
<p class="paragraph"> </p>
<h4>Controlling Load Order </h4>
<p class="paragraph">如果所依赖的插件不能被解析的话，则依赖于此的插件将被放弃并且不会被加载，这就是所谓的“强”依赖。然而我们可以通过使用 
loadAfter来定义一个“弱”依赖，示例如下：
</p>
<p class="paragraph"> </p>
<div class="code">
<pre>def loadAfter = ['controllers']</pre>
</div>
<p class="paragraph">此处如果 <code>controllers</code> 
插件存在的话，插件将在controllers之后被加载，否则的话将被单独加载. 插件也可以适应于其他已存在的插件，以Hibernate插件的 
<code>doWithSpring</code>闭包代码为例：
</p>
<p class="paragraph"> </p>
<div class="code">
<pre><span class="java-keyword">if</span>(manager?.hasGrailsPlugin(<span class="java-quote">"controllers"</span>)) {
        openSessionInViewInterceptor(OpenSessionInViewInterceptor) {
                flushMode = HibernateAccessor.FLUSH_MANUAL
                sessionFactory = sessionFactory
        }
        grailsUrlHandlerMapping.interceptors &lt;&lt; openSessionInViewInterceptor
  }</pre>
</div>
<p class="paragraph">这里，<code>controllers</code>插件如果被加载的话，Hibernate插件仅仅注册一个<code>OpenSessionInViewInterceptor</code> 
变量manager是 <a class="api">GrailsPluginManager</a> 
interface 接口的一个实例，并且提供同其他插件交互的方法，而且 <code>GrailsPluginManager</code> 
本身存在与任何一个插件中。</p>
  </div>

  


  
  
  <div id="bottoms" class="clearfix">
    
    <div id="share_weibo">分享到：
      <a data-type='sina' href="javascript:;" title="分享到新浪微博"><img src="/images/sina.jpg"></a>
      <a data-type='qq' href="javascript:;" title="分享到腾讯微博"><img src="/images/tec.jpg"></a>
    </div>
  </div>

  <div class="blog_nav">
    <div class="pre_next">
      <a href="/blog/705557" class="next" title="Grails Web服务">Grails Web服务</a>
      |
      <a href="/blog/705555" class="pre" title="Grails 安全性">Grails 安全性</a>
    </div>
  </div>
  <div class="blog_bottom">
    <ul>
      <li>2010-07-05 08:19:15</li>
      <li>浏览 503</li>
      <li><a href="#comments">评论(1)</a></li>
      
      
      <li>分类:<a href="http://www.iteye.com/blogs/category/language">编程语言</a></li>      
      <li class='last'><a href="http://www.iteye.com/wiki/blog/705556" target="_blank" class="more">相关推荐</a></li>
    </ul>
  </div>

  <div class="blog_comment">
    <h5>评论</h5>
    <a id="comments" name="comments"></a>
    <div id="bc1804248">
  <div class="comment_title">
    1 楼
    <a href='http://miao19880124.iteye.com' target='_blank'>miao19880124</a>
    2010-12-15&nbsp;&nbsp;
    
    
  </div>
  <div class="comment_content">你把文档复制上有意思么？？</div>
</div>


    
    
  </div>

  <div class="blog_comment">
    <h5>发表评论</h5>
            <p style="text-align:center; margin-top:30px;margin-bottom:0px;"><a href="/login" style="background-color:white;"> <img src="/images/login_icon.png" style="vertical-align:middle; margin-right: 10px;" /></a><a href="/login">  您还没有登录,请您登录后再发表评论 </a></p>
      </div>
</div>


<script type="text/javascript">
  dp.SyntaxHighlighter.HighlightAll('code', true, true);

  $$('#main .blog_content pre[name=code]').each(function(pre, index){ // blog content
    var post_id = 705556;
    var location = window.location;
    source_url = location.protocol + "//" + location.host + location.pathname + location.search;
    pre.writeAttribute('codeable_id', post_id);
    pre.writeAttribute('codeable_type', "Blog");
    pre.writeAttribute('source_url', source_url);
    pre.writeAttribute('pre_index', index);
    pre.writeAttribute('title', 'Grails 插件');
  });
  $$('#main .blog_comment > div').each(function(comment){// comment
    var post_id = comment.id.substr(2);
    $$("#"+comment.id+" pre[name=code]").each(function(pre, index){
      var location = window.location;
      source_url = location.protocol + "//" + location.host + location.pathname + location.search;
      source_url += "#" + comment.id;
      pre.writeAttribute('codeable_id', post_id);
      pre.writeAttribute('codeable_type', "BlogComment");
      pre.writeAttribute('source_url', source_url);
      pre.writeAttribute('pre_index', index);
      pre.writeAttribute('title', 'Grails 插件');
    });
  });
  code_favorites_init();

  fix_image_size($$('div.blog_content img'), 700);
  function quote_comment(id) {
    new Ajax.Request('/editor/quote', {
      parameters: {'id':id, 'type':'BlogComment'},
      onSuccess:function(response){editor.bbcode_editor.textarea.insertAfterSelection(response.responseText);
        Element.scrollTo(editor.bbcode_editor.textarea.element);}
    });
  }

  new WeiboShare({share_buttons: $('share_weibo'), img_scope: $('blog_content')});
</script>




        </div>

        <div id="local">
          <div class="local_top"></div>
          <div id="blog_owner">
  <div id="blog_owner_logo"><a href='http://justjavac.iteye.com'><img alt="justjavac的博客" class="logo" src="http://www.iteye.com/upload/logo/user/277041/59c752b0-7fe9-32d2-9bf0-b7dc1a018120.jpg?1279005094" title="justjavac的博客: justjavac(风之谷)" /></a></div>
  <div id="blog_owner_name">justjavac</div>
</div>

          <div id="blog_actions">
            <ul>
              <li>浏览: 647227 次</li>
              <li>性别: <img alt="Icon_minigender_1" src="http://www.iteye.com/images/icon_minigender_1.gif?1324994303" title="男" /></li>
              <li>来自: 天津</li>
              <li><img src='/images/status/offline.gif'/></li>
              
            </ul>
          </div>
          <div id="user_visits" class="clearfix">
            <h5>最近访客 <span style='font-weight:normal;font-size:12px;padding-left:30px;'><a href="/blog/user_visits">更多访客&gt;&gt;</a></span></h5>
            
              <div class="user_visit">
                <div class="logo"><a href='http://fireqiao.iteye.com' target='_blank'><img alt="fireqiao的博客" class="logo" src="http://www.iteye.com/images/user-logo-thumb.gif?1324994303" title="fireqiao的博客: " /></a></div>
                <div class="left"><a href='http://fireqiao.iteye.com' target='_blank'>fireqiao</a></div>
              </div>
            
              <div class="user_visit">
                <div class="logo"><a href='http://guoguo007.iteye.com' target='_blank'><img alt="guoguo007的博客" class="logo" src="http://www.iteye.com/images/user-logo-thumb.gif?1324994303" title="guoguo007的博客: " /></a></div>
                <div class="left"><a href='http://guoguo007.iteye.com' target='_blank'>guoguo007</a></div>
              </div>
            
              <div class="user_visit">
                <div class="logo"><a href='http://robindut.iteye.com' target='_blank'><img alt="robindut的博客" class="logo" src="http://www.iteye.com/images/user-logo-thumb.gif?1324994303" title="robindut的博客: " /></a></div>
                <div class="left"><a href='http://robindut.iteye.com' target='_blank'>robindut</a></div>
              </div>
            
              <div class="user_visit">
                <div class="logo"><a href='http://nicklasy.iteye.com' target='_blank'><img alt="nicklasy的博客" class="logo" src="http://www.iteye.com/upload/logo/user/58139/d6540abd-73cd-31f4-a428-03394c505b57-thumb.jpg?1236836131" title="nicklasy的博客: java" /></a></div>
                <div class="left"><a href='http://nicklasy.iteye.com' target='_blank'>nicklasy</a></div>
              </div>
            
          </div>

          
            <div>
              <h5>博客专栏</h5>
              

  
  <dl class="series" style="padding-bottom:5px;">
    <dt>
      <a href="/blogs/subjects/justjavac" target="_blank"><img alt="7eca6ee3-d299-3924-8342-02305099fd96" src="http://www.iteye.com/upload/logo/blog_wiki/603203/7eca6ee3-d299-3924-8342-02305099fd96.jpg?1323410246" /></a>
    </dt>
    <dd>
    <a href="/blogs/subjects/justjavac" target="_blank" title="实战 Groovy">实战 Groovy</a><br />
      <span class="visited">浏览量：2028</span>
    </dd>
  </dl>
  


            </div>
          

                      <div id="blog_menu">
              <h5>文章分类</h5>
              <ul>
                <li><a href="/">全部博客 (370)</a></li>
                
                  <li><a href="/category/27974">程序员 (52)</a></li>
                
                  <li><a href="/category/28272">BT编程 (7)</a></li>
                
                  <li><a href="/category/28320">Java (66)</a></li>
                
                  <li class='selected'><a href="/category/112093">Android (16)</a></li>
                
                  <li><a href="/category/29623">Groovy&amp;Grails (42)</a></li>
                
                  <li><a href="/category/39325">C/C++ (4)</a></li>
                
                  <li><a href="/category/102515">PHP/Perl/Python (19)</a></li>
                
                  <li><a href="/category/102517">html/css (53)</a></li>
                
                  <li><a href="/category/111588">yaml (1)</a></li>
                
                  <li><a href="/category/111589">CodeIgniter (13)</a></li>
                
                  <li><a href="/category/111860">JQuery (6)</a></li>
                
                  <li><a href="/category/31030">经典文章 (49)</a></li>
                
                  <li><a href="/category/39195">笑话 (4)</a></li>
                
                  <li><a href="/category/102516">其他 (31)</a></li>
                
                  <li><a href="/category/116117">javascript (13)</a></li>
                
                  <li><a href="/category/205678">云计算 (0)</a></li>
                
              </ul>
            </div>
            <div id='month_blogs'>
              <h5>社区版块</h5>
              <ul>
                <li><a href="/blog/news">我的资讯</a> (9)</li>
                <li>
                  <a href="/blog/post">我的论坛</a> (26)
                </li>
                <li><a href="/blog/solution">我解决的问题</a> (1)</li>
              </ul>
            </div>
            <div id="month_blogs">
              <h5>存档分类</h5>
              <ul>
                
                  <li><a href="/blog/monthblog/2012-02">2012-02</a> (1)</li>
                
                  <li><a href="/blog/monthblog/2011-12">2011-12</a> (4)</li>
                
                  <li><a href="/blog/monthblog/2011-11">2011-11</a> (3)</li>
                
                <li><a href="/blog/monthblog_more">更多存档...</a></li>
              </ul>
            </div>
            
            
              <div id="comments_top">
                <h5>评论排行榜</h5>
                <ul>
                  
                    <li><a href="/blog/1142742" title="[转载]谈谈2011年度最佳代码">[转载]谈谈2011年度最佳代码</a></li>
                  
                    <li><a href="/blog/1144151" title="数字签名是什么？">数字签名是什么？</a></li>
                  
                    <li><a href="/blog/1067079" title="最易出错的javascript面试题">最易出错的javascript面试题</a></li>
                  
                    <li><a href="/blog/1073139" title="清除代码异味">清除代码异味</a></li>
                  
                    <li><a href="/blog/1061287" title="这样去写你的 HTML">这样去写你的 HTML</a></li>
                  
                </ul>
              </div>
            

            <div id="guest_books">
              <h5>最新评论</h5>
              <ul>
                
                <li>
                  <a href='http://pywepe.iteye.com' target='_blank'>pywepe</a>： 
                    楼主的文章拷贝自 重构重构一书很强调提炼函数,不过出经常提 ...<br />
                  <a href="/blog/1407833#bc2243670">何时提炼函数 &amp; 用查询取代临时变量</a>
                </li>
                
                <li>
                  <a href='http://zzh0731-live-cn.iteye.com' target='_blank'>五点晨曦</a>： 
                  函数嵌套得太多一样很麻烦的，我也不喜欢一句话函数。我提取函数的 ...<br />
                  <a href="/blog/1407833#bc2243625">何时提炼函数 &amp; 用查询取代临时变量</a>
                </li>
                
                <li>
                  <a href='http://zui4yi1.iteye.com' target='_blank'>zui4yi1</a>： 
                  面向过程有面向过程的优点，面向对象有面向对象的优点。把参数都提 ...<br />
                  <a href="/blog/1407833#bc2243509">何时提炼函数 &amp; 用查询取代临时变量</a>
                </li>
                
                <li>
                  <a href='http://greatghoul.iteye.com' target='_blank'>greatghoul</a>： 
                  greatghoul 写道觉得没有提炼之前更容易理解一些吧。不 ...<br />
                  <a href="/blog/1407833#bc2243444">何时提炼函数 &amp; 用查询取代临时变量</a>
                </li>
                
                <li>
                  <a href='http://greatghoul.iteye.com' target='_blank'>greatghoul</a>： 
                  觉得没有提炼之前更容易理解一些吧。<br />
                  <a href="/blog/1407833#bc2243443">何时提炼函数 &amp; 用查询取代临时变量</a>
                </li>
                
              </ul>
            </div>

            <div class="local_bottom"></div>
          
        </div>
      </div>

      <div id="footer" class="clearfix">
        <div id="copyright">
          <hr/>
          声明：ITeye文章版权属于作者，受法律保护。没有作者书面许可不得转载。若作者同意转载，必须以超链接形式标明文章原始出处和作者。<br />
          &copy; 2003-2011 ITeye.com.   All rights reserved.  [ 京ICP证110151号  京公网安备110105010620 ]
        </div>
      </div>
    </div>
    <script type="text/javascript">
  document.write("<img src='http://stat.iteye.com/?url="+ encodeURIComponent(document.location.href) + "&referrer=" + encodeURIComponent(document.referrer) + "&user_id=' width='0' height='0' />");
</script>

    
<script type="text/javascript">

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-8784195-1']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();

</script>
      
  </body>
</html>
